<!DOCTYPE html>
<!--
Copyright (c) 2013 The Chromium Authors. All rights reserved.
Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file.
-->
<link rel="import" href="/tracing/base/settings.html">

<script>
'use strict';

tr.exportTo('tr.model', function() {
  const Settings = tr.b.Settings;

  /**
   * A way to persist settings specific to parts of a trace model.
   *
   * This object should not be persisted because it builds up internal data
   * structures that map model objects to settings keys. It should thus be
   * created for the duration of whatever interaction(s) you're going to do with
   * model settings, and then discarded.
   *
   * This system works on a notion of an object key: for an object's key, it
   * considers all the other keys in the model. If it is unique, then the key is
   * persisted to tr.b.Settings. However, if it is not unique, then the
   * setting is stored on the object itself. Thus, objects with unique keys will
   * be persisted across page reloads, whereas objects with nonunique keys will
   * not.
   */
  function ModelSettings(model) {
    this.model = model;
    this.objectsByKey_ = [];
    this.nonuniqueKeys_ = [];
    this.buildObjectsByKeyMap_();
    this.removeNonuniqueKeysFromSettings_();
    this.ephemeralSettingsByGUID_ = {};
  }

  ModelSettings.prototype = {
    buildObjectsByKeyMap_() {
      const objects = [];
      this.model.iterateAllPersistableObjects(function(o) {
        objects.push(o);
      });

      const objectsByKey = {};
      const NONUNIQUE_KEY = 'nonuniqueKey';
      for (let i = 0; i < objects.length; i++) {
        const object = objects[i];
        const objectKey = object.getSettingsKey();
        if (!objectKey) continue;
        if (objectsByKey[objectKey] === undefined) {
          objectsByKey[objectKey] = object;
          continue;
        }
        objectsByKey[objectKey] = NONUNIQUE_KEY;
      }

      const nonuniqueKeys = {};
      Object.keys(objectsByKey).forEach(function(objectKey) {
        if (objectsByKey[objectKey] !== NONUNIQUE_KEY) {
          return;
        }
        delete objectsByKey[objectKey];
        nonuniqueKeys[objectKey] = true;
      });

      this.nonuniqueKeys = nonuniqueKeys;
      this.objectsByKey_ = objectsByKey;
    },

    removeNonuniqueKeysFromSettings_() {
      const settings = Settings.get('trace_model_settings', {});
      let settingsChanged = false;
      Object.keys(settings).forEach(function(objectKey) {
        if (!this.nonuniqueKeys[objectKey]) {
          return;
        }
        settingsChanged = true;
        delete settings[objectKey];
      }, this);
      if (settingsChanged) {
        Settings.set('trace_model_settings', settings);
      }
    },

    hasUniqueSettingKey(object) {
      const objectKey = object.getSettingsKey();
      if (!objectKey) return false;
      return this.objectsByKey_[objectKey] !== undefined;
    },

    getSettingFor(object, objectLevelKey, defaultValue) {
      const objectKey = object.getSettingsKey();
      if (!objectKey || !this.objectsByKey_[objectKey]) {
        const settings = this.getEphemeralSettingsFor_(object);
        const ephemeralValue = settings[objectLevelKey];
        if (ephemeralValue !== undefined) {
          return ephemeralValue;
        }
        return defaultValue;
      }

      const settings = Settings.get('trace_model_settings', {});
      if (!settings[objectKey]) {
        settings[objectKey] = {};
      }
      const value = settings[objectKey][objectLevelKey];
      if (value !== undefined) {
        return value;
      }
      return defaultValue;
    },

    setSettingFor(object, objectLevelKey, value) {
      const objectKey = object.getSettingsKey();
      if (!objectKey || !this.objectsByKey_[objectKey]) {
        this.getEphemeralSettingsFor_(object)[objectLevelKey] = value;
        return;
      }

      const settings = Settings.get('trace_model_settings', {});
      if (!settings[objectKey]) {
        settings[objectKey] = {};
      }
      if (settings[objectKey][objectLevelKey] === value) {
        return;
      }
      settings[objectKey][objectLevelKey] = value;
      Settings.set('trace_model_settings', settings);
    },

    getEphemeralSettingsFor_(object) {
      if (object.guid === undefined) {
        throw new Error('Only objects with GUIDs can be persisted');
      }
      if (this.ephemeralSettingsByGUID_[object.guid] === undefined) {
        this.ephemeralSettingsByGUID_[object.guid] = {};
      }
      return this.ephemeralSettingsByGUID_[object.guid];
    }
  };

  return {
    ModelSettings,
  };
});
</script>
